package com.hc.programming.thread;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author huangchao E-mail:fengquan8866@163.com
 * @version 创建时间：2024/6/16 21:18
 */
public class 线程交替输出 {
    private static int[] numbers = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26};
    private static char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();

    public static void main(String[] args) throws InterruptedException {
//        method1();
        method2();
//        method3();
//        method4();
//        method5();
//        method6();
    }

    /**
     * TransferQueue
     */
    private static void method6() {
        TransferQueue<Object> queue = new LinkedTransferQueue<>();
        new Thread(() -> {
            try {
                for (int n : numbers) {
                    System.out.print(queue.take());
                    queue.transfer(n);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1").start();
        new Thread(() -> {
            try {
                for (char c : chars) {
                    queue.transfer(c);
                    System.out.print(queue.take());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2").start();
    }

    /**
     * ReentrantLock + Condition
     */
    private static void method5() {
        Lock lock = new ReentrantLock();
        Condition cd1 = lock.newCondition();
        Condition cd2 = lock.newCondition();

        Thread t1 = new Thread(() -> {
            lock.lock();
            try {
                for (int n : numbers) {
                    System.out.print(n);
                    cd2.signal();
                    cd1.await();
                }
                cd2.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                lock.lock();
                for (char c : chars) {
                    System.out.print(c);
                    cd1.signal();
                    cd2.await();
                }
                cd1.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
        t1.start();
        t2.start();
    }

    /**
     * CountDownLatch + synchronized
     */
    private static void method4() {
        Object o = new Object();
        CountDownLatch cdl = new CountDownLatch(1);
        Thread t1 = new Thread(() -> {
            cdl.countDown();
            synchronized (o) {
                for (int i = 0; i < numbers.length; i++) {
                    System.out.print(numbers[i]);
                    try {
                        o.notify();
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                o.notify();
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                cdl.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o) {
                for (int i = 0; i < chars.length; i++) {
                    System.out.print(chars[i]);
                    try {
                        o.notify();
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                o.notify();
            }
        });
        t1.start();
        t2.start();
    }

    /**
     * synchronized + for + wait + notify
     */
    private static void method3() throws InterruptedException {
        Object o = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (o) {
                for (int i = 0; i < numbers.length; i++) {
                    System.out.print(numbers[i]);
                    try {
                        o.notify();
                        if (i != numbers.length - 1) {
                            o.wait();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        Thread t2 = new Thread(() -> {
            synchronized (o) {
                for (int i = 0; i < chars.length; i++) {
                    System.out.print(chars[i]);
                    try {
                        o.notify();
                        if (i != chars.length - 1) {
                            o.wait();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t1.start();
        Thread.sleep(1);
        t2.start();
    }

    static Thread t1 = null, t2 = null;

    /**
     * LockSupport
     * park、unpark不需要管顺序
     */
    private static void method2() {
        t1 = new Thread(() -> {
            for (int n : numbers) {
                System.out.print(n);
                LockSupport.unpark(t2);
                LockSupport.park();
            }
        });
        t2 = new Thread(() -> {
            for (char c : chars) {
                LockSupport.park();
                System.out.print(c);
                LockSupport.unpark(t1);
            }
        });
        t1.start();
        t2.start();
    }

    /**
     * 方案一: while + synchronized + notify + wait + AtomicInteger
     */
    private static void method1() throws InterruptedException {
        AtomicInteger i = new AtomicInteger();
        Object o = new Object();
        Thread t1 = new Thread(() -> {
            while (true) {
                synchronized (o) {
                    System.out.print(numbers[i.get()]);
                    try {
                        o.notify();
                        if (i.get() < 25) {
                            o.wait();
                        } else {
                            break;
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        Thread t2 = new Thread(() -> {
            while (true) {
                synchronized (o) {
                    System.out.print(chars[i.getAndIncrement()]);
                    try {
                        if (i.get() < 26) {
                            o.notify();
                            o.wait();
                        } else {
                            break;
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t1.start();
        Thread.sleep(1);
        t2.start();
    }
}
